/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.core.runtime;

import org.proteusframework.core.api.*;
import org.proteusframework.core.api.ext.IAdapterManager;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.api.model.IVersion;
import org.proteusframework.core.api.model.IVersionCollection;
import org.proteusframework.core.base.Namespace;
import org.proteusframework.core.base.NoSuchNamespaceException;
import org.proteusframework.core.base.NoSuchVersionException;
import org.proteusframework.core.base.Version;
import org.proteusframework.core.spi.IProteusRegistrarFactory;
import org.proteusframework.core.spi.ProteusRegistrarFactory;
import org.proteusframework.core.util.NameUtil;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.logging.Logger;

/**
 * Primary entry point for a Proteus Framework application.
 *
 * @author Tacoma Four
 */
public class Proteus
{
    private static final Logger logger = Logger.getLogger(Proteus.class.getCanonicalName());

    public static void main(String[] args)
    {
        Proteus p = new Proteus();
        p.run(args);
    }

    public void run(String[] args)
    {

        Map<INamespace, IProteusRegistrarFactory> registrar = new HashMap<INamespace, IProteusRegistrarFactory>();

        if (args.length < 7)
        {
            System.out.println("\nWelcome to the Proteus Framework\nVersion 1.0.0\n");
            System.out.println("Usage: java -jar proteus.jar jar namespace id version package class path\n");
            System.out.println("where:\n");
            System.out.println("jar      \tPath to the .jar file that defines the Proteus Registrar Factory");
            System.out.println("namespace\tNamespace of the target plugin to extract an interface from");
            System.out.println("id       \tID of the target plugin to extract an interface from");
            System.out.println("version  \tVersion of the API to extract in the form of MAJOR.MINOR");
            System.out.println("package  \tPackage name to assign to the code generated interface");
            System.out.println("class    \tClass name to assign to the code generated interface");
            System.out.println("directory\tDirectory where the code generated file will be written");
            System.out.println("jars*    \tOne or more jar files, space separated, to search for registrars");
            System.out.println("\nNOTE: All parameters are required");
        }

        try
        {

            URL[] urls = new URL[args.length - 6];

            for (int i = 6; i < args.length; i++)
            {
                File f = new File(args[i]);
                urls[i - 6] = f.toURI().toURL();
            }

            URLClassLoader classLoader = new URLClassLoader(urls);

            IPlatformDelegate delegate = new IPlatformDelegate()
            {
                @Override
                public <T extends IRuntimeEngine> T getEngine(Class<T> engineClassType)
                {
                    return null;
                }

                @Override
                public IFactoryRegistrar getFactoryRegistrar()
                {
                    return null;
                }

                @Override
                public IPluginRegistrar getPluginRegistrar(INamespace qualifiedName) throws NoSuchNamespaceException
                {
                    return null;
                }

                @Override
                public IAdapterManager getAdapterManager()
                {
                    return null;
                }

                @Override
                public <T extends IPlatformDelegate> T adapt(Class<T> delegateClassType)
                {
                    return null;
                }
            };


            ServiceLoader<ProteusRegistrarFactory> loader = ServiceLoader.load(ProteusRegistrarFactory.class, classLoader);

            for (ProteusRegistrarFactory registrarFactory : loader)
            {
                String displayName = registrarFactory.getDisplayableName().toString();
                logger.info("Registering factory " + displayName + " with " + ProteusRegistrar.class.getName() + ".");

                registrar.put(registrarFactory.getDisplayableName(), registrarFactory);
            }

            INamespace namespace = new Namespace(args[0], args[1]);
            logger.info("Looking for qualified name: " + NameUtil.getCanonicalName(namespace));

            int major = Integer.parseInt(args[2].substring(0, args[2].indexOf(".")));
            int minor = Integer.parseInt(args[2].substring(args[2].indexOf(".") + 1, args[2].length()));

            IVersion version = new Version(major, minor);
            logger.info("Looking for version: " + version.toString());

            IProteusRegistrarFactory registrarProvider = registrar.get(namespace);
            IPluginRegistrar pluginRegistrar = registrarProvider.createFactory(delegate, IPlatformDelegate.class);

            logger.info("Located plugin registrar... generating interface code");

            IVersionCollection collection = pluginRegistrar.getVersionCollection();
            IInterfaceEmitter vi = collection.getInterface(version);

            String classDefinition = vi.emit(args[3], args[4]);

            File outputDir = new File(args[5]);
            File outputFile = new File(outputDir, args[4].concat(".java"));


            FileWriter writer = new FileWriter(outputFile);
            writer.write(classDefinition);
            writer.flush();
            writer.close();

            logger.info("Successfully wrote interface to " + outputFile.getAbsolutePath());

        } catch (MalformedURLException e)
        {
            logger.severe("Unexpected exception: " + e.getMessage());
        } catch (NumberFormatException e)
        {
            logger.severe("Unable to parse " + args[2] + " into an IVersion");
        } catch (NoSuchVersionException e)
        {
            logger.severe("Unexpected exception: " + e.getMessage());
        } catch (IOException e)
        {
            logger.severe("Unexpected exception: " + e.getMessage());
        }
    }
}
